package com.sprouting.ops.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.sprouting.ops.interfaces.CacheKey;
import com.sprouting.ops.interfaces.ConstantBase;
import com.sprouting.ops.model.OpsAuthority;
import com.sprouting.ops.model.OpsRole;
import com.sprouting.ops.model.OpsUser;
import com.sprouting.ops.model.ResultJson;
import com.sprouting.ops.model.bo.LoginBo;
import com.sprouting.ops.model.properties.LoginProperties;
import com.sprouting.ops.model.vo.LoginSuccessVo;
import com.sprouting.ops.model.vo.UserSimpleVo;
import com.sprouting.ops.model.vo.VerificationCode;
import com.sprouting.ops.service.OpsAuthorityService;
import com.sprouting.ops.service.OpsRoleService;
import com.sprouting.ops.service.OpsUserService;
import com.sprouting.ops.utils.IdTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 开放控制器
 *
 * @author ：LX（长沙麓谷）
 * @date 创建时间： 2023/3/18 16:59
 */
@SaIgnore
@Slf4j
@RestController
@RequestMapping("open")
public class OpenController {

    @Autowired
    private OpsUserService opsUserService;
    @Autowired
    private OpsRoleService opsRoleService;
    @Autowired
    private OpsAuthorityService opsAuthorityService;
    @Autowired
    private LoginProperties loginProperties;


    /**
     * 获取验证码
     * @return
     */
    @PostMapping("/getVerificationCode")
    public ResultJson getVerificationCode(){
        // 这里使用原始固定的验证码，后面有需求在加上验证码
        VerificationCode verificationCode = new VerificationCode();
        verificationCode.setImages(null);
        verificationCode.setLoginPasswordKey(loginProperties.getKey());
        // 一般唯一值 放到缓存中，加上验证码
        verificationCode.setKeyId(IdTool.getId());
        return ResultJson.success(verificationCode);
    }


    /**
     * 获取用户加密解密的key
     * @return
     */
    @PostMapping("/getKey")
    public ResultJson<String> getKey(){
        return ResultJson.success(loginProperties.getKey());
    }

    /**
     * 获取当前时间
     * @return
     */
    @GetMapping("/getTime")
    public ResultJson getTime(){
        return ResultJson.success(DateUtil.now());
    }

    /**
     * 检查是否登录
     * @return
     */
    @GetMapping("checkLogin")
    public ResultJson checkLogin(){
        // 校验当前客户端是否已经登录，如果未登录则抛出 `NotLoginException` 异常
        StpUtil.checkLogin();
        return ResultJson.success();
    }

    /**
     * PC端登录
     * @param loginDTO 登录信息
     * @return
     */
    @PostMapping("loginPc")
    public ResultJson loginPc(@Validated LoginBo loginDTO){
        // 验证码
        if (loginDTO.getCode().equals(loginProperties.getVerificationCode())){
           log.info("永久有效的验证码请求");
        } else {
            /// 验证码校验

        }

        // 1、---解密
        String userName = opsUserService.decrypt(loginDTO.getUserName());
        String password = opsUserService.decrypt(loginDTO.getPassword());

        // 1.2 解密成功后还要进行加密判断密码是否一致
        OpsUser userDb = opsUserService.getByUserLoginName(userName);
        if (userDb == null){
            return ResultJson.error("账号或密码错误");
        }

        String msg = checkLogin(password, userDb);
        if (msg != null){
            return ResultJson.error(msg);
        }

        // 2、--- 登陆成功后
        StpUtil.login(userDb.getId(), ConstantBase.LoginType.PC);
        // 获取当前账号ID的session[有三种session，当前账号id的Session，当前token的专属Session，自定义Session指的是以一个特定的值作为SessionId来分配的Session]
        // 只使用当前用户ID的session
        // 3.1--- 加载到session中。优化点，如果为了提高性能，这里可以优化到其他接口
        SaSession session = StpUtil.getSession(true);
        // 用户信息
        session.set(CacheKey.SaTokenSession.SESSION_KEY_USER, userDb);
        // 角色信息
        List<OpsRole> roleList = opsRoleService.getRoleByUserId(userDb.getId());
        List<String> roleCodeList = roleList.stream().map(OpsRole::getCode).distinct().collect(Collectors.toList());
        session.set(CacheKey.SaTokenSession.SESSION_KEY_ROLE, JSONUtil.toJsonStr(roleCodeList));
        // 权限信息
        List<String> roleIdList = roleList.stream().map(OpsRole::getId).distinct().collect(Collectors.toList());
        List<OpsAuthority> powerList = opsAuthorityService.getPowerByRoleId(roleIdList);
        List<String> powerResourceList = powerList.stream().map(OpsAuthority::getCode).distinct().collect(Collectors.toList());
        session.set(CacheKey.SaTokenSession.SESSION_KEY_POWER, JSONUtil.toJsonStr(powerResourceList));

        // 3、封装返回数据
        LoginSuccessVo loginSuccessVo = new LoginSuccessVo();
        loginSuccessVo.setOpsRoleList(roleCodeList);
        loginSuccessVo.setPowerList(powerResourceList);
        UserSimpleVo userSimpleVo = BeanUtil.copyProperties(userDb, UserSimpleVo.class);
        loginSuccessVo.setOpsUser(userSimpleVo);
        loginSuccessVo.setTokenName(StpUtil.getTokenName());
        loginSuccessVo.setTokenValue(StpUtil.getTokenValue());

        // 封装返回的数据
        return ResultJson.success(loginSuccessVo);
    }

    /**
     * 检查是否可以登录
     * @param password 用户输入的密码-已解密
     * @param userDb 数据库查询出的账号信息
     * @return
     */
    private String checkLogin(String password, OpsUser userDb){
        // 错误次数检查
        // String loginNumKey = CacheKey.User.USER_LOGIN_NUM + userDb.getId();
        // if (loginProperties.getLoginErrorProperties().getOpen()){
        //     Object loginError = TimedCacheConfig.getTimedCache().get(loginNumKey);
        //     if (loginError != null){
        //         Integer loginNum = (Integer) loginError;
        //         if (loginProperties.getLoginErrorProperties().getNum() <= loginNum){
        //             // 已经被锁定
        //             // Long expire = redisStringTool.getExpire(redisKey, TimeUnit.MINUTES);
        //             // return String.format("用户已被锁定，请稍后重试，剩余%s分钟!", expire);
        //             return "用户已被锁定，请稍后重试";
        //         }
        //     }
        // }

        int errorNum = 0;
        // 加盐加密后的密码
        String passwordSalt = SaSecureUtil.md5BySalt(password, userDb.getSalt());
        if (!userDb.getPassword().equals(passwordSalt)){
            errorNum ++;
            // 增加一次错误
            // TimedCacheConfig.getTimedCache().put(loginNumKey, errorNum, loginProperties.getLoginErrorProperties().getTime() * 1000);
            return "用户名或密码错误";
        }

        // 删除所有输错的信息，直接将过期设置为1毫秒
        // TimedCacheConfig.getTimedCache().remove(loginNumKey);

        if (ConstantBase.UserStatus.STOP.equals(userDb.getStatus())){
            return "账号已停用，请联系管理员";
        }
        return null;
    }



}
